package org.bigtree.example.common.utils;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author bigTree
 * @since 2025/4/24
 */
public final class JsonUtils {
    private static final String DATE_FORMAT = "yyyy-MM-dd";
    private static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT);
    private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern(DATETIME_FORMAT);
    private static final Gson GSON = new GsonBuilder()
            .setDateFormat(DATE_FORMAT)
            .setPrettyPrinting()
            .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeAdapter())
            .registerTypeAdapter(LocalDate.class, new LocalDateAdapter())
            .create();

    private JsonUtils() {}

    public static String toJson(Object obj) {
        return GSON.toJson(obj);
    }

    public static <R> R toBean(String jsonStr, Class<R> clazz) {
        return GSON.fromJson(jsonStr, clazz);
    }

    public static <T> List<T> toList(String jsonStr, TypeToken<List<T>> typeToken) {
        return GSON.fromJson(jsonStr, typeToken.getType());
    }

    public static <K, V> Map<K, V> toMap(String jsonStr, TypeToken<Map<K, V>> typeToken) {
        return GSON.fromJson(jsonStr, typeToken.getType());
    }

    private static class LocalDateAdapter extends TypeAdapter<LocalDate> {
        @Override
        public void write(JsonWriter out, LocalDate value) throws IOException {
            if(Objects.isNull(value)) {
                out.nullValue();
            } else {
                out.value(DATE_FORMATTER.format(value));
            }
        }

        @Override
        public LocalDate read(JsonReader in) throws IOException {
            if (Objects.equals(in.peek(), JsonToken.NULL)) {
                in.nextNull();
                return null;
            } else {
                return LocalDate.parse(in.nextString(), DATE_FORMATTER);
            }
        }
    }

    private static class LocalDateTimeAdapter extends TypeAdapter<LocalDateTime> {
        @Override
        public void write(JsonWriter out, LocalDateTime value) throws IOException {
            if(Objects.isNull(value)) {
                out.nullValue();
            } else {
                out.value(DATETIME_FORMATTER.format(value));
            }

        }

        @Override
        public LocalDateTime read(JsonReader in) throws IOException {
            if (Objects.equals(in.peek(), JsonToken.NULL)) {
                in.nextNull();
                return null;
            } else {
                return LocalDateTime.parse(in.nextString(), DATETIME_FORMATTER);
            }
        }
    }
}
